package com.hy.lightning.boot.web.servlet;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;

import com.hy.lightning.boot.JettyServer;
import com.hy.lightning.boot.core.ContentType;
import com.hy.lightning.boot.core.RequestException;
import com.hy.lightning.boot.core.ServiceConfiguration;
import com.hy.lightning.boot.core.ServicesFactory;
import com.hy.lightning.boot.web.HttpRequestCache;
import com.hy.lightning.boot.web.filter.FilterHandler;
import com.hy.lightning.boot.web.servlet.bean.DefaultHttpServletRequest;
import com.hy.lightning.boot.web.servlet.bean.DefaultHttpServletResponse;
import com.hy.lightning.boot.web.servlet.bean.PR;
import com.hy.lightning.boot.web.servlet.bean.RequestBody;
import com.hy.lightning.boot.web.servlet.impl.URLPathHandler;
import com.hy.lightning.commons.utils.JSON;
import com.hy.lightning.commons.utils.ObjectUtil;

@WebServlet("/")
public class ServletMapper extends HttpServlet {

	private static final long serialVersionUID = 1L;

	private Logger logger = Logger.getLogger(this.getClass());

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) {
		RequestBody requestBody = new RequestBody();
		try {
			requestBody = doService(req);
			writeAndFlushSuccess(resp, requestBody.getValue(), requestBody.getType());
		} catch (JsonParseException e) {
			logger.error(e, e);
			writeAndFlushError(resp, 0, "JsonParseException, 参数解析异常，请查看参数中的json对象格式是否正确！", requestBody.getType());
		} catch (JsonMappingException e) {
			logger.error(e, e);
			writeAndFlushError(resp, 0, "JsonMappingException, 参数解析异常，请查看参数中的json对象格式是否正确！", requestBody.getType());
		} catch (ClassNotFoundException e) {
			logger.error(e, e);
			writeAndFlushError(resp, 0, "ClassNotFoundException, 服务未注册，请检查是否注册服务或调用服务路径是否正确！", requestBody.getType());
		} catch (InstantiationException e) {
			logger.error(e, e);
			writeAndFlushError(resp, 0, "InstantiationException, 服务实例化异常，请查看参数名称及参数类型是否正确", requestBody.getType());
		} catch (IllegalAccessException e) {
			logger.error(e, e);
			writeAndFlushError(resp, 0, "IllegalAccessException, 服务实例化异常，请查看参数名称及参数类型是否正确", requestBody.getType());
		} catch (NoSuchMethodException e) {
			logger.error(e, e);
			writeAndFlushError(resp, 0, "NoSuchMethodException, 服务方法未找到，请检查是否注册服务或调用服务路径是否正确！", requestBody.getType());
		} catch (SecurityException e) {
			logger.error(e, e);
			writeAndFlushError(resp, 0, "SecurityException, 服务实例化异常，请查看参数名称及参数类型是否正确", requestBody.getType());
		} catch (IllegalArgumentException e) {
			logger.error(e, e);
			writeAndFlushError(resp, 0, "IllegalArgumentException,参数不合法，无法调用对于服务，请检查基本数据类型(short,int,long,double,float)参数是否存在null值", requestBody.getType());
		} catch (InvocationTargetException e) {
			logger.error(e.getCause(), e.getCause());
			String message =  e.getCause().getMessage();
			String resutDesc = e.getCause() + "服务端异常，请查看参数名称及参数类型是否正确";
			if(!ObjectUtil.isNullOrEmpty(message) && message.contains(":")) {
				int state = Integer.valueOf(message.split(":")[0].trim());
				resutDesc = message.split(":")[1];
				writeAndFlushError(resp, state, resutDesc, requestBody.getType());
			}
		} catch (RequestException e) {
			logger.error(e.getResultdesc(), e);
			writeAndFlushError(resp, e.getResultstate(), e.getResultdesc(), requestBody.getType());
		} catch (IOException e) {
			logger.error(e, e);
			writeAndFlushError(resp, 0, "IOException，请查看参数名称及参数类型是否正确", requestBody.getType());
		}
	}

	private void writeAndFlushError(HttpServletResponse resp, int resultstate, String resultdesc, String contentType) {
		if(ObjectUtil.isNullOrEmpty(contentType)) contentType = ContentType.JSON;
		resp.setContentType(contentType);
		if(ContentType.JSON.equals(contentType)) {
			PR pr = new PR(resultstate, "", resultdesc);
			PrintWriter writer = null;
			try {
				String responseBody = JSON.toString(pr);
				writer = resp.getWriter();
				writer.print(responseBody);
				writer.flush();
			} catch (IOException e) {
				logger.error(e, e);
			} finally {
				if(writer != null) {
					writer.close();
				}
			}
		} else {
			resultstate = 500;
			try {
				resp.sendError(resultstate, resultdesc);
			} catch (IOException e) {
				logger.error(e, e);
			}
		}
		
	}
	
	private void writeAndFlushSuccess(HttpServletResponse resp, Object obj, String contentType) {
		if(ObjectUtil.isNullOrEmpty(contentType)) contentType = ContentType.JSON;
		resp.setContentType(contentType);
		if(obj == null) obj = "";
		if(ContentType.JSON.equals(contentType) && obj.getClass() != PR.class) {
			obj = new PR(1, obj, "OK");
		}
		String filterHandlerClassPath = JettyServer.serverInitContext.getProperty("filterHandler");
		OutputStream out = null;
		try {
			FilterHandler filterHandler = (FilterHandler) Class.forName(filterHandlerClassPath).newInstance();
			byte[] bytes = filterHandler.responseFilter((DefaultHttpServletResponse)resp, obj);
			out = resp.getOutputStream();
			out.write(bytes);
			out.flush();
		} catch (InstantiationException | IllegalAccessException | ClassNotFoundException | IOException e) {
			logger.error(e, e);
		} finally {
			try {
				if(out != null)
					out.close();
			} catch (IOException e) {
				logger.error(e, e);
			}
		}
	}

	/**
	 * 获取服务配置
	 * 
	 * @param requestService
	 * @return
	 * @throws RequestException
	 */
	private ServiceConfiguration getServiceConfiguration(String requestService) throws RequestException {
		ServiceConfiguration serviceConfiguration = ServicesFactory.servicesCache.get(requestService);
		if (serviceConfiguration == null)
			throw new RequestException(0, "", "调用服务不存在");
		return serviceConfiguration;
	}

	/**
	 * 开始反射调用服务
	 * 
	 * @param req
	 * @return
	 * @throws RequestException
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 * @throws IOException
	 * @throws JsonMappingException
	 * @throws JsonParseException
	 */
	private RequestBody doService(HttpServletRequest req) throws RequestException, ClassNotFoundException,
			InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException,
			IllegalArgumentException, InvocationTargetException, JsonParseException, JsonMappingException, IOException {
		RequestBody requestBody = new RequestBody();
		URLPathHandler urlPathHandler = new URLPathHandler(req.getServletPath());
		String requestService = urlPathHandler.getRequestService();
		String requestMethod = urlPathHandler.getMethod();
		ServiceConfiguration serviceConfiguration = getServiceConfiguration(requestService);
		requestBody.setType(serviceConfiguration.getRespType());
		Class<?> c = Class.forName(serviceConfiguration.getClassPath());
		Object object = c.newInstance();
		try {
			HttpRequestCache.setRequest(object, (DefaultHttpServletRequest)req);
			Class<?>[] paramTypes = serviceConfiguration.getParamTypes();
			Method method = null;
			if (paramTypes == null || paramTypes.length == 0) {
				method = c.getDeclaredMethod(requestMethod);
				requestBody.setValue(method.invoke(object));
			} else {
				method = c.getDeclaredMethod(requestMethod, paramTypes);
				Object[] argValues = getParamValues(req, serviceConfiguration);
				requestBody.setValue(method.invoke(object, argValues));
			}
		} finally {
			HttpRequestCache.close(object);
		}
		return requestBody;
	}

	private Object[] getParamValues(HttpServletRequest request, ServiceConfiguration serviceConfiguration)
			throws JsonParseException, JsonMappingException, IOException {
		Class<?>[] paramTypes = serviceConfiguration.getParamTypes();
		String[] paramNames = serviceConfiguration.getParamNames();
		Object[] values = new Object[paramTypes.length];
		if (paramTypes == null || paramTypes.length == 0)
			return null;
		if (DefaultHttpServletRequest.class == paramTypes[0]) {
			return new Object[] { request };
		}
		for (int i = 0; i < paramTypes.length; i++) {
			values[i] = transformType(paramTypes[i], request.getParameter(paramNames[i]));
		}
		return values;
	}

	public Object transformType(Class<?> paramType, String value)
			throws JsonParseException, JsonMappingException, IOException {
		if (ObjectUtil.isNullOrEmpty(value))
			return value;
		Object resultObj = null;
		if (paramType == Integer.class || paramType == int.class)
			resultObj = Integer.valueOf(value);
		else if (paramType == Short.class || paramType == short.class)
			resultObj = Short.valueOf(value);
		else if (paramType == Long.class || paramType == long.class)
			resultObj = Long.valueOf(value);
		else if (paramType == Double.class || paramType == double.class)
			resultObj = Double.valueOf(value);
		else if (paramType == Float.class || paramType == float.class)
			resultObj = Float.valueOf(value);
		else if (paramType == Boolean.class || paramType == boolean.class)
			resultObj = Boolean.valueOf(value);
		else if (paramType == String.class)
			resultObj = value;
		else {
			resultObj = JSON.toObj(value, paramType);
		}
		return resultObj;
	}
}
